/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <devmon/driver_abi.h>
#include <dlfcn.h>
#include <gtest/gtest.h>
#include <test_utilities/test_base.h>

#include "../../../drivers/bus/hisport/hisport.h"
#include "../../../drivers/internal/manager.h"

using namespace dev;

namespace drivers {
namespace bus {
namespace hisport {
namespace test {

class hisport_test_service : public mc::engine::service {
public:
    hisport_test_service(const std::string& name) : mc::engine::service(name) {
    }
};

class HisportAbiTest : public mc::test::TestWithEngine {
public:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service.init();
        m_test_service.start();
        load_driver();
    }
    static void TearDownTestSuite() {
        m_test_service.stop();
        if (m_driver_handle != nullptr) {
            dlclose(m_driver_handle);
            m_driver_handle = nullptr;
        }
        TestWithEngine::TearDownTestSuite();
    }
    static void load_driver() {
        m_driver_handle = dlopen("./opt/bmc/drivers/libHisport.so", RTLD_LAZY);
        if (m_driver_handle == nullptr) {
            MC_THROW(mc::system_exception, "Failed to load Hisport driver: ${error}",
                     ("error", dlerror()));
        }
        register_device_driver_func func = reinterpret_cast<register_device_driver_func>(
            dlsym(m_driver_handle, "register_device_driver"));
        MC_ASSERT_THROW(func, mc::bad_function_call_exception,
                        "Failed to get register_device_driver function: ${error}",
                        ("error", dlerror()));
        device_driver_t* devices       = nullptr;
        uint8_t          devices_count = 0;
        status_t         ret           = func(&devices, &devices_count);
        MC_ASSERT_THROW(ret == STATUS_OK, mc::bad_function_call_exception,
                        "Failed to get device driver manager: ${error}", ("error", ret));
        m_devices.clear();
        m_devices.reserve(devices_count);
        for (uint8_t i = 0; i < devices_count; i++) {
            auto device_driver = std::make_shared<device_driver_t>(devices[i]);
            m_devices.emplace(device_driver->device_name, device_driver);
        }
    }
    mc::dict create_test_connector(uint8_t system_id, uint8_t position, uint8_t slot) {
        return mc::dict{{"SystemId", system_id}, {"Position", position}, {"Slot", slot}};
    }
    mc::dict create_test_csr_object(const std::string& object_name, uint8_t id, uint8_t bus_num,
                                    uint8_t reg_num, uint8_t flag_num, uint8_t send_reg_addr,
                                    uint8_t recv_reg_addr, uint8_t flag_reg_addr,
                                    uint8_t max_batch_len, uint32_t max_data_len,
                                    uint8_t max_offset_width, uint8_t max_length_width) {
        return mc::dict{{"object_name", object_name},
                        {"Id", id},
                        {"BusNum", bus_num},
                        {"RegNum", reg_num},
                        {"FlagNum", flag_num},
                        {"SendRegAddr", send_reg_addr},
                        {"RecvRegAddr", recv_reg_addr},
                        {"FlagRegAddr", flag_reg_addr},
                        {"MaxBatchLen", max_batch_len},
                        {"MaxDataLen", max_data_len},
                        {"MaxOffsetWidth", max_offset_width},
                        {"MaxLengthWidth", max_length_width}};
    }

    // 创建内部Hisport对象
    void create_internal_hisport_object(const std::string& object_name, uint8_t id, uint8_t bus_num,
                                        uint8_t reg_num, uint8_t flag_num, uint8_t send_reg_addr,
                                        uint8_t recv_reg_addr, uint8_t flag_reg_addr,
                                        uint8_t max_batch_len, uint32_t max_data_len,
                                        uint8_t max_offset_width, uint8_t max_length_width) {
        auto&            manager = dev::device_manager::get_instance();
        mc::mutable_dict csr_data;
        csr_data["Id"]             = id;
        csr_data["BusNum"]         = bus_num;
        csr_data["RegNum"]         = reg_num;
        csr_data["FlagNum"]        = flag_num;
        csr_data["SendRegAddr"]    = send_reg_addr;
        csr_data["RecvRegAddr"]    = recv_reg_addr;
        csr_data["FlagRegAddr"]    = flag_reg_addr;
        csr_data["MaxBatchLen"]    = max_batch_len;
        csr_data["MaxDataLen"]     = max_data_len;
        csr_data["MaxOffsetWidth"] = max_offset_width;
        csr_data["MaxLengthWidth"] = max_length_width;

        auto bus_ptr = manager.create_bus(object_name, "Hisport", csr_data);
        ASSERT_TRUE(bus_ptr != nullptr);
    }

    static hisport_test_service                                              m_test_service;
    static std::unordered_map<std::string, std::shared_ptr<device_driver_t>> m_devices;
    static void*                                                             m_driver_handle;
};
hisport_test_service HisportAbiTest::m_test_service{"bmc.kepler.test_hisport"};
std::unordered_map<std::string, std::shared_ptr<device_driver_t>> HisportAbiTest::m_devices;
void* HisportAbiTest::m_driver_handle = nullptr;

// ========================= 驱动注册功能测试 =========================
TEST_F(HisportAbiTest, register_device_driver_success) {
    EXPECT_TRUE(m_devices.find("Hisport") != m_devices.end());
}
TEST_F(HisportAbiTest, device_driver_structure_validation) {
    for (const auto& [device_name, device_driver] : m_devices) {
        EXPECT_FALSE(std::string(device_driver->device_name).empty());
        EXPECT_TRUE(device_driver->ctor != nullptr);
        EXPECT_TRUE(device_driver->init != nullptr);
    }
}
// ========================= Hisport设备创建测试 =========================
TEST_F(HisportAbiTest, create_hisport_success) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport");
    ASSERT_TRUE(device != nullptr);
    Hisport_object* hisport_device = static_cast<Hisport_object*>(device);
    EXPECT_TRUE(hisport_device != nullptr);
}
TEST_F(HisportAbiTest, create_hisport_null_service) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(nullptr, "test_hisport");
    EXPECT_TRUE(device == nullptr);
}
TEST_F(HisportAbiTest, create_hisport_null_name) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, nullptr);
    EXPECT_TRUE(device == nullptr);
}
// ========================= Hisport设备初始化测试 =========================
TEST_F(HisportAbiTest, init_hisport_success) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_init");
    ASSERT_TRUE(device != nullptr);

    // 先创建内部Hisport对象
    std::string object_name = "test_hisport_internal";
    create_internal_hisport_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);

    auto csr_object =
        create_test_csr_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);
    auto     connector = create_test_connector(1, 1, 1);
    status_t ret       = device_driver->init(device, &csr_object, &connector);
    EXPECT_EQ(ret, STATUS_OK);
}

TEST_F(HisportAbiTest, init_hisport_without_internal_object) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_no_internal");
    ASSERT_TRUE(device != nullptr);

    // 创建测试配置，但不创建内部对象
    auto csr_object = create_test_csr_object("non_existent_hisport", 1, 128, 10, 2, 0x00, 0x10,
                                             0x1A, 2, 1024, 2, 2);
    auto connector  = create_test_connector(1, 1, 1);

    // 应该返回STATUS_ERROR，因为内部对象不存在
    status_t ret = device_driver->init(device, &csr_object, &connector);
    EXPECT_EQ(ret, STATUS_ERROR);
}

TEST_F(HisportAbiTest, init_hisport_null_params) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_null_params");
    ASSERT_TRUE(device != nullptr);
    status_t ret = device_driver->init(device, nullptr, nullptr);
    EXPECT_EQ(ret, STATUS_ERROR);
}

TEST_F(HisportAbiTest, init_hisport_null_device) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto csr_object = create_test_csr_object("test_hisport_null_device", 1, 128, 10, 2, 0x00, 0x10,
                                             0x1A, 2, 1024, 2, 2);
    auto connector  = create_test_connector(1, 1, 1);
    status_t ret    = device_driver->init(nullptr, &csr_object, &connector);
    EXPECT_EQ(ret, STATUS_ERROR);
}
// ========================= Hisport属性验证测试 =========================
TEST_F(HisportAbiTest, hisport_properties_validation) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_properties");
    ASSERT_TRUE(device != nullptr);
    std::string object_name           = "test_hisport_properties_internal";
    uint8_t     test_id               = 2;
    uint8_t     test_bus_num          = 129;
    uint8_t     test_reg_num          = 15;
    uint8_t     test_flag_num         = 3;
    uint8_t     test_send_reg_addr    = 0x01;
    uint8_t     test_recv_reg_addr    = 0x11;
    uint8_t     test_flag_reg_addr    = 0x1B;
    uint8_t     test_max_batch_len    = 3;
    uint32_t    test_max_data_len     = 2048;
    uint8_t     test_max_offset_width = 3;
    uint8_t     test_max_length_width = 3;

    // 创建内部Hisport对象
    create_internal_hisport_object(object_name, test_id, test_bus_num, test_reg_num, test_flag_num,
                                   test_send_reg_addr, test_recv_reg_addr, test_flag_reg_addr,
                                   test_max_batch_len, test_max_data_len, test_max_offset_width,
                                   test_max_length_width);

    auto csr_object = create_test_csr_object(
        object_name, test_id, test_bus_num, test_reg_num, test_flag_num, test_send_reg_addr,
        test_recv_reg_addr, test_flag_reg_addr, test_max_batch_len, test_max_data_len,
        test_max_offset_width, test_max_length_width);
    auto     connector = create_test_connector(1, 1, 1);
    status_t init_ret  = device_driver->init(device, &csr_object, &connector);
    ASSERT_EQ(init_ret, STATUS_OK);
    Hisport_object* hisport_device = reinterpret_cast<Hisport_object*>(device);
    ASSERT_TRUE(hisport_device != nullptr);
    EXPECT_EQ(hisport_device->m_interface_hisport.Id, test_id);
    EXPECT_EQ(hisport_device->m_interface_hisport.BusNum, test_bus_num);
    EXPECT_EQ(hisport_device->m_interface_hisport.RegNum, test_reg_num);
    EXPECT_EQ(hisport_device->m_interface_hisport.FlagNum, test_flag_num);
    EXPECT_EQ(hisport_device->m_interface_hisport.SendRegAddr, test_send_reg_addr);
    EXPECT_EQ(hisport_device->m_interface_hisport.RecvRegAddr, test_recv_reg_addr);
    EXPECT_EQ(hisport_device->m_interface_hisport.FlagRegAddr, test_flag_reg_addr);
    EXPECT_EQ(hisport_device->m_interface_hisport.MaxBatchLen, test_max_batch_len);
    EXPECT_EQ(hisport_device->m_interface_hisport.MaxDataLen, test_max_data_len);
    EXPECT_EQ(hisport_device->m_interface_hisport.MaxOffsetWidth, test_max_offset_width);
    EXPECT_EQ(hisport_device->m_interface_hisport.MaxLengthWidth, test_max_length_width);
}

// ========================= 底层驱动功能测试 =========================
TEST_F(HisportAbiTest, hisport_driver_memory_operations) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_memory");
    ASSERT_TRUE(device != nullptr);

    // 创建内部Hisport对象
    std::string object_name = "test_hisport_memory_internal";
    create_internal_hisport_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);

    auto csr_object =
        create_test_csr_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);
    auto     connector = create_test_connector(1, 1, 1);
    status_t init_ret  = device_driver->init(device, &csr_object, &connector);
    ASSERT_EQ(init_ret, STATUS_OK);

    Hisport_object* hisport_device = static_cast<Hisport_object*>(device);

    // 测试写入和读取操作
    std::string          test_data = "\x01\x02\x03\x04";
    std::vector<uint8_t> test_data_vec(test_data.begin(), test_data.end());
    ACCESS_DATA_S        write_data = {};
    write_data.addr                 = 0x10;
    write_data.offset               = 0x20;
    write_data.offset_width         = 2;
    write_data.length               = 4;
    write_data.in_buffer            = test_data_vec;

    uint32_t write_ret = hisport_device->m_interface_hisport.m_bus_hisport->write(write_data);
    EXPECT_EQ(write_ret, 0);

    ACCESS_DATA_S read_data = {};
    read_data.addr          = 0x10;
    read_data.offset        = 0x20;
    read_data.offset_width  = 2;
    read_data.length        = 4;

    uint32_t read_ret = hisport_device->m_interface_hisport.m_bus_hisport->read(read_data);
    EXPECT_EQ(read_ret, 0);
    std::string read_result(read_data.out_buffer.begin(), read_data.out_buffer.end());
    EXPECT_EQ(read_result, test_data);
}

TEST_F(HisportAbiTest, hisport_driver_parameter_validation) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_validation");
    ASSERT_TRUE(device != nullptr);

    // 创建内部Hisport对象
    std::string object_name = "test_hisport_validation_internal";
    create_internal_hisport_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);

    auto csr_object =
        create_test_csr_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);
    auto     connector = create_test_connector(1, 1, 1);
    status_t init_ret  = device_driver->init(device, &csr_object, &connector);
    ASSERT_EQ(init_ret, STATUS_OK);

    Hisport_object* hisport_device = static_cast<Hisport_object*>(device);

    // 测试偏移量宽度验证
    std::string          test_invalid_data = "\x01\x02\x03\x04";
    std::vector<uint8_t> test_invalid_data_vec(test_invalid_data.begin(), test_invalid_data.end());
    ACCESS_DATA_S        invalid_offset_data = {};
    invalid_offset_data.addr                 = 0x10;
    invalid_offset_data.offset               = 0x20;
    invalid_offset_data.offset_width         = 5; // 超过最大值4
    invalid_offset_data.length               = 4;
    invalid_offset_data.in_buffer            = test_invalid_data_vec;

    EXPECT_THROW(hisport_device->m_interface_hisport.m_bus_hisport->write(invalid_offset_data),
                 mc::invalid_arg_exception);
    EXPECT_THROW(hisport_device->m_interface_hisport.m_bus_hisport->read(invalid_offset_data),
                 mc::invalid_arg_exception);
}

TEST_F(HisportAbiTest, hisport_driver_bus_operations) {
    auto device_driver = m_devices["Hisport"];
    ASSERT_TRUE(device_driver != nullptr);
    auto device = device_driver->ctor(&m_test_service, "test_hisport_bus");
    ASSERT_TRUE(device != nullptr);

    // 创建内部Hisport对象
    std::string object_name = "test_hisport_bus_internal";
    create_internal_hisport_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);

    auto csr_object =
        create_test_csr_object(object_name, 1, 128, 10, 2, 0x00, 0x10, 0x1A, 2, 1024, 2, 2);
    auto     connector = create_test_connector(1, 1, 1);
    status_t init_ret  = device_driver->init(device, &csr_object, &connector);
    ASSERT_EQ(init_ret, STATUS_OK);

    Hisport_object* hisport_device = static_cast<Hisport_object*>(device);

    // 测试总线锁定和解锁操作
    EXPECT_NO_THROW(hisport_device->m_interface_hisport.m_bus_hisport->bus_lock());
    EXPECT_NO_THROW(hisport_device->m_interface_hisport.m_bus_hisport->bus_unlock());

    // 测试获取ID
    EXPECT_EQ(hisport_device->m_interface_hisport.m_bus_hisport->get_id(), 1);
}

} // namespace test
} // namespace hisport
} // namespace bus
} // namespace drivers